package cz.jm.web.jmlib.client.dialog.items;

import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.HasMouseOutHandlers;
import com.google.gwt.event.dom.client.HasMouseOverHandlers;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;

import cz.jm.web.jmlib.client.dialog.validation.HasValidationValue;
import cz.jm.web.jmlib.client.dialog.validation.MultipleValidator;
import cz.jm.web.jmlib.client.dialog.validation.Validator;
import cz.jm.web.jmlib.client.resources.Resources;

/** Bazova trieda pre polozky dialogov ( ako combobox, input ... ).
 *  GWT poskytuje tieto nastroje len v zakladnej forme. Zdedene polozky 
 *  od tejto triedy su zlozene z troch casti: 
 *  <ul><li>popisok </li><li>ovladaci prvok</li><li>ikona validnosti</li></ul>.
 *	Dalej je pridana schopnost validacie hodnoty a zobrazenia stavu validacie. 
 * */
public abstract class DialogItem extends Composite implements HasValidationValue, MouseOverHandler, MouseOutHandler {
	
 	/** Defaultna farba textu v popisku polozky pokial bola validacia neuspesna. */
	public static final String DEFAULT_WARNING_LABEL_COLOR = "orange";
	/** Defaultna Farba textu v popisku polozky pokial bola validacia uspesna. */	
	public static final String DEFAULT_NORMAL_LABEL_COLOR = "black";

	protected static final String PREFERED_MASTER_ENTRY_WIDTH = "150px";
	protected static final String PREFERED_MASTER_ENTRY_HEIGHT = "18px";

	
	
	/** Rozmer ikony pre placeholder aby widget nemenil velkost pri zobrazovani a schovavani ikony. */
	private static final String WARN_ICON_SIZE = "32px";
	/** Sirka labelu prevne zadana a nie podla textu. Pokial je podla textu tak sa viacero 
	 * poloziek pod sebou nezarovna. V pripade potreby je mozne sirku dodatocne zmenit.*/
	private static final String DEFAULT_LABEL_WIDTH = "135px";
	private static final int DEFAULT_ITEM_PADDING = 1; // PX

	
	/** Zakladny panel vlastniaci vsteko.*/
	private final HorizontalPanel mainWidget = new HorizontalPanel();
	/** Popisok polozky dialogu. */
	private final Label label = new Label("Label");
	/** Ikona ktora sa zobrazuje pokial zadana hodnota nepresla validaciou. */
	private final Image warnIcon = new Image(Resources.INSTANCE.getWarning());
	/** Panel obsahujuci hint pre nezdarenu validaciu. napoveda uzivatelovi co je zle. */
	private final PopupPanel validationWarningHintPopup = new PopupPanel(true);
	/** Skupina validatorov pridruzenych k tejto polozke. */
	private final MultipleValidator validation = new MultipleValidator();	

	
	/** Farba popisku pokial je hodnota validna resp. este nebola validovana. */	
	private String normalLabelColor = DEFAULT_NORMAL_LABEL_COLOR;
	/** Farba popisku pokial je hodnota validna resp. este nebola validovana. */
	private String warningLabelColor = DEFAULT_WARNING_LABEL_COLOR;
	
	public DialogItem() {
		initWidget(mainWidget);
		
		label.setWidth(DEFAULT_LABEL_WIDTH);
		label.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);		

		warnIcon.setVisible(false);
		
		addWarningHint(warnIcon);
		getElement().getStyle().setPadding(DEFAULT_ITEM_PADDING, Unit.PX);
		
    }
	
	/** Nastavenie nastroja (combobox, input ...). */
	public void setDialogMasterEntry(Widget masterEntry) {
		// Najskor popisok.
		addCompositeEntry(label);
		// Potom hlavny nastroj.
		addCompositeEntry(masterEntry);
		// Nakoniec ikona s varovanim pre nepodarenu validaciu.
		// Ikona se zneviditelnuje pokial je validacia OK. Potom sa meni rozmer 
		// polozky co neposobi dobre. Preto sa vklada najskor placeholder kt. nie je vidno ale 
		// drzi spravny rozmer a az don sa vlozi ikona.
		FlowPanel placeHolder = new FlowPanel();
		placeHolder.setSize(WARN_ICON_SIZE, WARN_ICON_SIZE);
		placeHolder.add(warnIcon);
		
		addCompositeEntry(placeHolder);		
	}
	
	/** Vlozenie casti vizualizovanej  polozky. */
	private void addCompositeEntry(Widget w) {
		mainWidget.add(w);		
		mainWidget.setCellVerticalAlignment(w,HasVerticalAlignment.ALIGN_MIDDLE);			
		mainWidget.setCellHorizontalAlignment(w, HasHorizontalAlignment.ALIGN_CENTER);
	}
	
	/** Reakcia na nepodarenu validaciu. Zmena farby popisku + zobrazenie ikony s varovanim. */
	public void onValidationFailure(String failMessage) {
		label.getElement().getStyle().setColor(warningLabelColor);
		label.getElement().getStyle().setFontWeight(FontWeight.BOLD);
		
	}
	
	/** Reakcia na nepodarenu validaciu. Zmena farby popisku + schovanie ikony s varovanim. */
	public void onValidationSuccess() {		
		label.getElement().getStyle().setColor(normalLabelColor);
		label.getElement().getStyle().setFontWeight(FontWeight.NORMAL);
	}

	/** Pre nejaky ciel prida zobrazovanie a schovanie varovania pri najdeni 
	 * mysou nad ciel respektive opusteni mysi cielu. Pre zobrazovanie varovania 
	 * musi ciel implementovat HasMouseOverHandlers a pre automaticke schovanie 
	 * varovania po opusteni mysi musi ciel implementovat HasMouseOutHandlers. 
	 * Tieto rozhrania vsak nie su pozadovane formalne pri predani parametru. 
	 * Pokial ciel nieco neimplemetuje tak len nedostane pozadovanu funkcionalitu. */
	protected void addWarningHint(HasHandlers target) {
		if (target instanceof HasMouseOverHandlers) {
			((HasMouseOverHandlers)target).addMouseOverHandler(this);
		}
		if (target instanceof HasMouseOutHandlers) {
			((HasMouseOutHandlers)target).addMouseOutHandler(this);
		}
	}
	
	/** Popisok polozky. Nastavenai farby je vhodne realizovat pomocou {@linkplain #setNormalLabelColor(String)} a
	 *  {@linkplain #setWarningLabelColor(String)} a nie priamo na objekte Label. Pretoze farby popisku sa 
	 *  menia pri validacii podla tychto nastaveni. Ostatne nastavenia triedy Label su pouzitelne. */
	public Label getLabel() {
    	return label;
    }

	/** Ikona zobrazovana pri neuspesnej validacii. */
	public Image getWarnIcon() {
    	return warnIcon;
    }

	
	@Override
	public boolean isValid() {
		final boolean isValid = validation.validate();
		if (isValid) {
			warnIcon.setVisible(false);
			onValidationSuccess();
		}
		else {
			warnIcon.setVisible(true);
			onValidationFailure(validation.getAllValidationFailMessages());
		}
		return isValid;
	}

	@Override
	public void addValidator(Validator v) {
		validation.addValidator(v);
	}

	@Override
	public void onMouseOver(MouseOverEvent event) {
		final String failMessage = validation.getAllValidationFailMessages();
		if (!failMessage.isEmpty() && !validationWarningHintPopup.isShowing()) {
			validationWarningHintPopup.setWidget(new HTML(failMessage));
			validationWarningHintPopup.setPopupPosition(event.getClientX(), event.getClientY());
			validationWarningHintPopup.show();
		}
	}

	
	@Override
    public void onMouseOut(MouseOutEvent event) {
		if (validationWarningHintPopup.isShowing()) {
			validationWarningHintPopup.hide();
		}
	    
    }
	
	/** Farba popisku, ktora sa pouzije po zdarenej validacii. */
	public String getNormalLabelColor() {
	    return normalLabelColor;
    }

	/** Nastavenie farby popisku, ktora sa pouzije po zdarenej validacii. */
	public void setNormalLabelColor(String normalLabelColor) {
	    this.normalLabelColor = normalLabelColor;
    }

	/** Nastavenbie defaultu farby popisku, ktora sa pouzije po zdarenej validacii. */
	public void resetNormalLabelColor() {
	    this.normalLabelColor = DEFAULT_NORMAL_LABEL_COLOR;
    }

	/** Farba popisku, ktora sa pouzije po NEzdarenej validacii. */
	public String getWarningLabelColor() {
	    return warningLabelColor;
    }

	/** Nastavenie farby popisku, ktora sa pouzije po NEzdarenej validacii. */
	public void setWarningLabelColor(String warningLabelColor) {
	    this.warningLabelColor = warningLabelColor;
    }

	/** Nastavenbie defaultu farby popisku, ktora sa pouzije po NEzdarenej validacii. */
	public void resetWarningLabelColor() {
	    this.warningLabelColor = DEFAULT_WARNING_LABEL_COLOR;
    }

}
